"""
预读引擎 - 实现智能预读机制
提供顺序预读、随机预读和自适应预读策略
"""

import os
import sys
import time
import threading
from typing import List, Dict, Optional, Set, Tuple
from collections import defaultdict, deque
from enum import Enum
from dataclasses import dataclass

# 添加当前目录到Python路径
current_dir = os.path.dirname(os.path.abspath(__file__))
if current_dir not in sys.path:
    sys.path.insert(0, current_dir)

from statistics import Logger


class AccessPattern(Enum):
    """访问模式枚举"""
    SEQUENTIAL = "sequential"      # 顺序访问
    RANDOM = "random"             # 随机访问
    HOT_SPOT = "hotspot"          # 热点访问
    UNKNOWN = "unknown"           # 未知模式


@dataclass
class AccessRecord:
    """访问记录"""
    page_id: int
    access_time: float
    hit_from_cache: bool


@dataclass
class ReadAheadRequest:
    """预读请求"""
    page_ids: List[int]
    priority: int               # 优先级 (1-10, 10最高)
    pattern: AccessPattern
    created_time: float


class ReadAheadEngine:
    """预读引擎核心类"""
    
    def __init__(self, 
                 enable_readahead: bool = True,
                 window_size: int = 4,
                 max_readahead_pages: int = 16,
                 detection_threshold: int = 3):
        """
        初始化预读引擎
        
        Args:
            enable_readahead: 是否启用预读
            window_size: 默认预读窗口大小
            max_readahead_pages: 最大预读页面数
            detection_threshold: 模式检测阈值
        """
        self.enable_readahead = enable_readahead
        self.window_size = window_size
        self.max_readahead_pages = max_readahead_pages
        self.detection_threshold = detection_threshold
        
        # 访问历史记录
        self.access_history = deque(maxlen=100)  # 最近100次访问
        self.page_access_count = defaultdict(int)  # 页面访问计数
        self.last_access_page = None
        
        # 模式检测
        self.current_pattern = AccessPattern.UNKNOWN
        self.pattern_confidence = 0.0
        self.sequential_count = 0
        
        # 预读统计
        self.readahead_stats = {
            'requests_generated': 0,
            'pages_prefetched': 0,
            'hits': 0,                 # 预读命中次数
            'misses': 0,               # 预读未命中次数
            'hit_rate': 0.0
        }
        
        # 线程安全锁
        self._lock = threading.RLock()
        
        # 日志记录器
        self.logger = Logger()
        
        self.logger.info("READAHEAD", 0, f"ReadAhead engine initialized: window={window_size}, max_pages={max_readahead_pages}")
    
    def record_access(self, page_id: int, from_cache: bool = False):
        """
        记录页面访问
        
        Args:
            page_id: 被访问的页面ID
            from_cache: 是否从缓存命中
        """
        if not self.enable_readahead:
            return
        
        with self._lock:
            current_time = time.time()
            
            # 记录访问
            access_record = AccessRecord(
                page_id=page_id,
                access_time=current_time,
                hit_from_cache=from_cache
            )
            self.access_history.append(access_record)
            self.page_access_count[page_id] += 1
            
            # 更新预读统计
            if from_cache and self._was_prefetched(page_id):
                self.readahead_stats['hits'] += 1
            
            # 检测访问模式
            self._detect_access_pattern(page_id)
            
            # 更新最后访问页面
            self.last_access_page = page_id
    
    def get_readahead_requests(self, current_page_id: int) -> List[ReadAheadRequest]:
        """
        基于当前访问生成预读请求
        
        Args:
            current_page_id: 当前访问的页面ID
            
        Returns:
            List[ReadAheadRequest]: 预读请求列表
        """
        if not self.enable_readahead:
            return []
        
        with self._lock:
            requests = []
            
            # 根据访问模式生成预读请求
            if self.current_pattern == AccessPattern.SEQUENTIAL:
                requests.extend(self._generate_sequential_readahead(current_page_id))
            elif self.current_pattern == AccessPattern.HOT_SPOT:
                requests.extend(self._generate_hotspot_readahead(current_page_id))
            else:
                # 默认的保守预读策略
                requests.extend(self._generate_conservative_readahead(current_page_id))
            
            # 更新统计
            self.readahead_stats['requests_generated'] += len(requests)
            total_pages = sum(len(req.page_ids) for req in requests)
            self.readahead_stats['pages_prefetched'] += total_pages
            
            return requests
    
    def _detect_access_pattern(self, page_id: int):
        """检测访问模式"""
        if len(self.access_history) < self.detection_threshold:
            return
        
        # 检测顺序访问模式
        if self._is_sequential_access(page_id):
            self.sequential_count += 1
            if self.sequential_count >= self.detection_threshold:
                self.current_pattern = AccessPattern.SEQUENTIAL
                self.pattern_confidence = min(1.0, self.sequential_count / 10.0)
        else:
            self.sequential_count = 0
            
        # 检测热点访问模式
        if self._is_hotspot_access(page_id):
            self.current_pattern = AccessPattern.HOT_SPOT
            self.pattern_confidence = 0.8
        
        # 如果模式不明确，设为随机访问
        if self.pattern_confidence < 0.3:
            self.current_pattern = AccessPattern.RANDOM
    
    def _is_sequential_access(self, page_id: int) -> bool:
        """检测是否为顺序访问"""
        if self.last_access_page is None:
            return False
        
        # 检查页面ID是否递增
        diff = page_id - self.last_access_page
        return 1 <= diff <= 3  # 允许小幅跳跃
    
    def _is_hotspot_access(self, page_id: int) -> bool:
        """检测是否为热点访问"""
        if len(self.access_history) < 5:
            return False
        
        # 检查最近5次访问中是否有3次以上访问相同或相邻页面
        recent_pages = [record.page_id for record in list(self.access_history)[-5:]]
        adjacent_count = sum(1 for p in recent_pages if abs(p - page_id) <= 2)
        
        return adjacent_count >= 3
    
    def _generate_sequential_readahead(self, page_id: int) -> List[ReadAheadRequest]:
        """生成顺序预读请求"""
        requests = []
        
        # 根据访问模式调整窗口大小
        dynamic_window = min(
            self.window_size * int(self.pattern_confidence * 2),
            self.max_readahead_pages
        )
        
        if dynamic_window > 0:
            # 预读后续页面
            prefetch_pages = list(range(page_id + 1, page_id + 1 + dynamic_window))
            
            # 过滤已经在缓存中的页面（需要外部提供）
            prefetch_pages = [p for p in prefetch_pages if not self._is_likely_cached(p)]
            
            if prefetch_pages:
                request = ReadAheadRequest(
                    page_ids=prefetch_pages,
                    priority=8,  # 顺序访问高优先级
                    pattern=AccessPattern.SEQUENTIAL,
                    created_time=time.time()
                )
                requests.append(request)
                
                self.logger.info("READAHEAD", page_id, 
                               f"Sequential readahead: {len(prefetch_pages)} pages starting from {page_id + 1}")
        
        return requests
    
    def _generate_hotspot_readahead(self, page_id: int) -> List[ReadAheadRequest]:
        """生成热点预读请求"""
        requests = []
        
        # 预读周围的页面
        radius = 2
        prefetch_pages = []
        
        for offset in range(-radius, radius + 1):
            target_page = page_id + offset
            if target_page != page_id and target_page > 0:
                if not self._is_likely_cached(target_page):
                    prefetch_pages.append(target_page)
        
        if prefetch_pages:
            request = ReadAheadRequest(
                page_ids=prefetch_pages,
                priority=6,  # 热点访问中等优先级
                pattern=AccessPattern.HOT_SPOT,
                created_time=time.time()
            )
            requests.append(request)
            
            self.logger.info("READAHEAD", page_id, 
                           f"Hotspot readahead: {len(prefetch_pages)} pages around {page_id}")
        
        return requests
    
    def _generate_conservative_readahead(self, page_id: int) -> List[ReadAheadRequest]:
        """生成保守预读请求"""
        requests = []
        
        # 只预读下一个页面
        next_page = page_id + 1
        if not self._is_likely_cached(next_page):
            request = ReadAheadRequest(
                page_ids=[next_page],
                priority=3,  # 低优先级
                pattern=AccessPattern.UNKNOWN,
                created_time=time.time()
            )
            requests.append(request)
            
            self.logger.info("READAHEAD", page_id, f"Conservative readahead: page {next_page}")
        
        return requests
    
    def _is_likely_cached(self, page_id: int) -> bool:
        """简单检查页面是否可能在缓存中"""
        # 检查最近访问历史
        recent_pages = [record.page_id for record in list(self.access_history)[-10:]]
        return page_id in recent_pages
    
    def _was_prefetched(self, page_id: int) -> bool:
        """检查页面是否是预读的结果"""
        # 简化实现：如果页面在缓存中但最近没有直接访问，则认为是预读的
        recent_direct_access = [record.page_id for record in list(self.access_history)[-5:] 
                               if not record.hit_from_cache]
        return page_id not in recent_direct_access
    
    def update_hit_rate(self):
        """更新命中率统计"""
        with self._lock:
            total_requests = self.readahead_stats['hits'] + self.readahead_stats['misses']
            if total_requests > 0:
                self.readahead_stats['hit_rate'] = self.readahead_stats['hits'] / total_requests
    
    def get_statistics(self) -> Dict:
        """获取预读统计信息"""
        with self._lock:
            self.update_hit_rate()
            return {
                'enabled': self.enable_readahead,
                'current_pattern': self.current_pattern.value,
                'pattern_confidence': self.pattern_confidence,
                'window_size': self.window_size,
                'stats': self.readahead_stats.copy(),
                'recent_access_count': len(self.access_history)
            }
    
    def configure(self, **kwargs):
        """动态配置预读参数"""
        with self._lock:
            if 'enable_readahead' in kwargs:
                self.enable_readahead = kwargs['enable_readahead']
            if 'window_size' in kwargs:
                self.window_size = max(1, min(kwargs['window_size'], 32))
            if 'max_readahead_pages' in kwargs:
                self.max_readahead_pages = max(1, min(kwargs['max_readahead_pages'], 64))
            
            self.logger.info("READAHEAD", 0, f"Configuration updated: {kwargs}")


class AsyncReadAheadExecutor:
    """异步预读执行器"""
    
    def __init__(self, page_manager):
        self.page_manager = page_manager
        self.logger = Logger()
        self._executor_lock = threading.Lock()
        
        # 异步执行队列
        import queue
        self.queue_module = queue  # 保存模块引用供后续使用
        self.prefetch_queue = queue.Queue(maxsize=100)
        
        # 启动后台预读线程
        self.running = True
        self.prefetch_thread = threading.Thread(
            target=self._prefetch_worker,
            name="ReadAhead-Worker",
            daemon=True
        )
        self.prefetch_thread.start()
        
        self.logger.info("READAHEAD", 0, "Async readahead executor started")
    
    def submit_readahead_request(self, request: ReadAheadRequest):
        """提交预读请求"""
        try:
            self.prefetch_queue.put_nowait(request)
        except self.queue_module.Full:
            self.logger.warn("READAHEAD", 0, "Prefetch queue full, dropping request")
    
    def _prefetch_worker(self):
        """预读工作线程"""
        while self.running:
            try:
                # 获取预读请求
                request = self.prefetch_queue.get(timeout=1.0)
                
                # 执行预读
                self._execute_prefetch(request)
                
                # 标记任务完成
                self.prefetch_queue.task_done()
                
            except self.queue_module.Empty:
                continue
            except Exception as e:
                self.logger.error("READAHEAD", 0, f"Prefetch worker error: {e}")
    
    def _execute_prefetch(self, request: ReadAheadRequest):
        """执行预读操作"""
        start_time = time.time()
        success_count = 0
        
        for page_id in request.page_ids:
            try:
                # 异步读取页面到缓存
                page_data = self.page_manager.read_page(page_id)
                if page_data:
                    success_count += 1
            except Exception as e:
                self.logger.info("READAHEAD", page_id, f"Prefetch failed: {e}")
        
        execution_time = time.time() - start_time
        self.logger.info("READAHEAD", 0, 
                         f"Prefetched {success_count}/{len(request.page_ids)} pages in {execution_time:.3f}s")
    
    def stop(self):
        """停止预读执行器"""
        self.running = False
        if self.prefetch_thread.is_alive():
            self.prefetch_thread.join(timeout=2.0)
        self.logger.info("READAHEAD", 0, "Async readahead executor stopped")
